﻿using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Foundation.Collections;

namespace Wrox.ProCSharp.Common
{
  /// <summary>
  /// Implementation of IObservableMap that supports reentrancy for use as a default view
  /// model.
  /// </summary>
  public class ObservableDictionary : IObservableMap<string, object>
  {
    private class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs<string>
    {
      public ObservableDictionaryChangedEventArgs(CollectionChange change, string key)
      {
        this.CollectionChange = change;
        this.Key = key;
      }

      public CollectionChange CollectionChange { get; private set; }
      public string Key { get; private set; }
    }

    private Dictionary<string, object> _dictionary = new Dictionary<string, object>();
    public event MapChangedEventHandler<string, object> MapChanged;

    private void InvokeMapChanged(CollectionChange change, string key)
    {
      var eventHandler = MapChanged;
      if (eventHandler != null)
      {
        eventHandler(this, new ObservableDictionaryChangedEventArgs(change, key));
      }
    }

    public void Add(string key, object value)
    {
      this._dictionary.Add(key, value);
      this.InvokeMapChanged(CollectionChange.ItemInserted, key);
    }

    public void Add(KeyValuePair<string, object> item)
    {
      this.Add(item.Key, item.Value);
    }

    public bool Remove(string key)
    {
      if (this._dictionary.Remove(key))
      {
        this.InvokeMapChanged(CollectionChange.ItemRemoved, key);
        return true;
      }
      return false;
    }

    public bool Remove(KeyValuePair<string, object> item)
    {
      object currentValue;
      if (this._dictionary.TryGetValue(item.Key, out currentValue) &&
          Object.Equals(item.Value, currentValue) && this._dictionary.Remove(item.Key))
      {
        this.InvokeMapChanged(CollectionChange.ItemRemoved, item.Key);
        return true;
      }
      return false;
    }

    public object this[string key]
    {
      get
      {
        return this._dictionary[key];
      }
      set
      {
        this._dictionary[key] = value;
        this.InvokeMapChanged(CollectionChange.ItemChanged, key);
      }
    }

    public void Clear()
    {
      var priorKeys = this._dictionary.Keys.ToArray();
      this._dictionary.Clear();
      foreach (var key in priorKeys)
      {
        this.InvokeMapChanged(CollectionChange.ItemRemoved, key);
      }
    }

    public ICollection<string> Keys
    {
      get { return this._dictionary.Keys; }
    }

    public bool ContainsKey(string key)
    {
      return this._dictionary.ContainsKey(key);
    }

    public bool TryGetValue(string key, out object value)
    {
      return this._dictionary.TryGetValue(key, out value);
    }

    public ICollection<object> Values
    {
      get { return this._dictionary.Values; }
    }

    public bool Contains(KeyValuePair<string, object> item)
    {
      return this._dictionary.Contains(item);
    }

    public int Count
    {
      get { return this._dictionary.Count; }
    }

    public bool IsReadOnly
    {
      get { return false; }
    }

    public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
    {
      return this._dictionary.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
      return this._dictionary.GetEnumerator();
    }

    public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
    {
      int arraySize = array.Length;
      foreach (var pair in this._dictionary)
      {
        if (arrayIndex >= arraySize) break;
        array[arrayIndex++] = pair;
      }
    }
  }
}
